﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.IO;
using System.Net;
using System.Reflection;
using System.Windows.Forms;
using System.Linq;

namespace ImvuCacheViewer {
    public partial class Form1 {
        private readonly Dictionary<string, ProductInfo> productInfos = new Dictionary<string, ProductInfo>();
        private Dictionary<string, ProductInfo> productCache = new Dictionary<string, ProductInfo>();

        private void LoadProductCache()
        {
            productCache = new Dictionary<string, ProductInfo>();
            string[] files = Directory.GetFiles(ProgCachePath, "*.info");
            foreach (string file in files) {
                using (StreamReader sr = File.OpenText(file)) {
                    string line = sr.ReadToEnd();
                    var info = new ProductInfo(line);
                    productCache[info.ID] = info;
                }
            }
        }

        private void SaveProductInfo(ProductInfo productInfo) {
            string filename = Path.Combine(ProgCachePath, productInfo.ID + ".info");
            using (StreamWriter sw = File.CreateText(filename)) {
                sw.Write(productInfo.ToString());
            }
        }

        private Image _defaultImage;
        private Image DefaultImage {
            get {
                if (_defaultImage == null) {
                    const string streamName = "ImvuCacheViewer.Q.png";
                    using (Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(streamName))
                    {
                        if (stream != null)
                        {
                            _defaultImage = Image.FromStream(stream);
                        }
                    }
                    
                }
                return _defaultImage;
            }
        }

        private void InitLoadProducts() {
            gridProducts.Rows.Clear();
            var infos = new List<ProductInfo>(productInfos.Values);
            // amazed this works in 2.0
            Comparison<ProductInfo> comparison = (a, b) => Math.Sign(b.Date.Ticks - a.Date.Ticks);
            infos.Sort(comparison);
            foreach (ProductInfo info in infos) {
                DataGridViewRow newRow = new DataGridViewRow();
                newRow.Tag = info.ID;
                newRow.Height = 80;
                DataGridViewTextBoxCell cellID = new DataGridViewTextBoxCell();
                cellID.Value = info.ID;
                newRow.Cells.Add(cellID);

                DataGridViewImageCell cellImage = new DataGridViewImageCell();
                cellImage.Value = DefaultImage;
                newRow.Cells.Add(cellImage);

                DataGridViewLinkCell cellName = new DataGridViewLinkCell();
                cellName.UseColumnTextForLinkValue = false;
                cellName.Value = string.IsNullOrEmpty(info.Name) ? "An IMVU Product" : info.Name;
                newRow.Cells.Add(cellName);

                DataGridViewTextBoxCell cellCreator = new DataGridViewTextBoxCell();
                cellCreator.Value = string.IsNullOrEmpty(info.Creator) ? "A Creator" : info.Creator;
                newRow.Cells.Add(cellCreator);

                DataGridViewTextBoxCell cellDate = new DataGridViewTextBoxCell();
                if (DateTime.Now.Date == info.Date) {
                    cellDate.Value = info.Date.ToString("h:mm tt");
                } else if (DateTime.Now.Year == info.Date.Year) {
                    cellDate.Value = info.Date.ToString("MMM d (HH:mm)");
                } else {
                    cellDate.Value = info.Date.ToString("MM/dd/yyyy HH:mm");
                }
                newRow.Cells.Add(cellDate);
                gridProducts.Rows.Add(newRow);
            }
        }

        private void GetProductInfo(ProductInfo productInfo) {
            WebClient webClient = new WebClient();
            if (string.IsNullOrEmpty(productInfo.ImageUrl))
            {
                string url = "http://www.imvu.com/shop/product.php?products_id=" + productInfo.ID;
                try {
                    StringReader sr = new StringReader(webClient.DownloadString(url));
                    string line;
                    bool getTitle = false;

                    while ((line = sr.ReadLine()) != null) {

                        if (getTitle) {
                            string[] pieces = line.Split(new[] { " by " },
                                                         StringSplitOptions.RemoveEmptyEntries);
                            if (pieces.Length == 2) {
                                productInfo.Name = pieces[0].Replace("IMVU Product: ", string.Empty);
                                productInfo.Creator = pieces[1].Replace("</title>", string.Empty);
                            }
                            getTitle = false;
                        }

                        if (line.Contains("<title>")) {
                            getTitle = true;
                        }

                        if (line.Contains("img id=\"product-image\"")) {
                            int posLeft = line.IndexOf("http");
                            int posRight = line.IndexOf("\"", posLeft);
                            productInfo.ImageUrl = line.Substring(posLeft, posRight - posLeft);
                        }
                    }
                    SaveProductInfo(productInfo);
                } catch (Exception exc) {
                    txtDebug.Text = txtDebug.Text + exc.Message + Environment.NewLine;
                    return;
                }
            }

            if (!string.IsNullOrEmpty(productInfo.ImageUrl) && productInfo.Image == null) {
                try {
                    byte[] bytes = webClient.DownloadData(productInfo.ImageUrl);
                    string filename = Path.Combine(ProgCachePath, productInfo.ID + ".img");
                    if (File.Exists(filename)) File.Delete(filename);
                    using (var fs = new FileStream(filename, FileMode.CreateNew, FileAccess.ReadWrite)) {
                        fs.Write(bytes, 0, bytes.Length);
                        fs.Close();
                    }
                }
                catch (Exception exc) {
                    txtDebug.Text = txtDebug.Text + exc.Message + Environment.NewLine;
                }
            }
        }

        private void LazyLoadProductInfo() {
            var infos = new List<ProductInfo>(productInfos.Values);
            // amazed this works in 2.0
            Comparison<ProductInfo> comparison = (a, b) => Math.Sign(b.Date.Ticks - a.Date.Ticks);
            infos.Sort(comparison);
            Queue<ProductInfo> qInfo = new Queue<ProductInfo>();
            foreach (ProductInfo info in infos) {
                if (string.IsNullOrEmpty(info.ImageUrl)) {
                    qInfo.Enqueue(info);
                }
            }

            const int threadCount = 20;
            ProductInfoWorker[] bw = new ProductInfoWorker[threadCount];
            for (int i = 0; i < threadCount; i++) {
                if (qInfo.Count <= 0) continue;
                bw[i] = new ProductInfoWorker();
                bw[i].ProductInfo = qInfo.Dequeue();
                bw[i].DoWork += (sender, e) => {
                    ProductInfoWorker thisWorker = sender as ProductInfoWorker;
                    if (thisWorker == null) return;
                    if (thisWorker.ProductInfo == null) return;
                    ProductInfo info = thisWorker.ProductInfo;
                    try {
                        GetProductInfo(info);
                        ProductInfo localInfo = info;
                        MethodInvoker mi = delegate {
                            DataGridViewRow row = gridProducts.Rows.Cast<DataGridViewRow>().FirstOrDefault(viewRow => viewRow.Tag.ToString() == localInfo.ID);
                            if (row != null)
                            {
                                if (string.IsNullOrEmpty(localInfo.Name))
                                {
                                    localInfo.Name = "Access Pass Product";
                                    localInfo.ImageUrl = "hidden";
                                }
                                row.Cells[colName.Index].Value = localInfo.Name;

                                if (string.IsNullOrEmpty(localInfo.Creator))
                                {
                                    localInfo.Creator = "-Unknown-";
                                }
                                row.Cells[colCreator.Index].Value = localInfo.Creator;
                                if (localInfo.Image != null)
                                {
                                    row.Cells[colImage.Index].Value = DefaultImage;
                                }
                                else
                                {
                                    if (!string.IsNullOrEmpty(localInfo.Name))
                                    {
                                        localInfo.ImageUrl = "hidden";
                                    }
                                }
                            }
                        };
                        if (InvokeRequired) Invoke(mi);
                        else mi();
                        SaveProductInfo(info);
                    }
                    catch (Exception exc) {
                        txtDebug.Text = txtDebug.Text + exc.Message + Environment.NewLine;
                    }
                                                           
                };
                bw[i].RunWorkerCompleted += (sender, e) => {
                    ProductInfoWorker thisWorker = sender as ProductInfoWorker;
                    if (thisWorker == null) return;
                    if (qInfo.Count > 0) {
                        thisWorker.ProductInfo = qInfo.Dequeue();
                        thisWorker.RunWorkerAsync();
                    }
                };
                bw[i].RunWorkerAsync();
            }
        }

        private void debugGetProductTest(object sender, EventArgs e)
        {
            GetProductInfo(new ProductInfo { ID = "1234567", Date = DateTime.Now });
        }

        private void bwProduct_DoWork(object sender, DoWorkEventArgs e)
        {
            LazyLoadProductInfo();
        }

    }

    public class ProductInfoWorker: BackgroundWorker {
        public ProductInfo ProductInfo { get; set; }
    }

}